Add a build manifest build step#6869
Add a build manifest build step#6869alfonso-noriega wants to merge 1 commit into02-wire-build-config-into-extension-specsfrom
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
146db7f to
e56e3c3
Compare
a3c7106 to
038b372
Compare
e56e3c3 to
ed19082
Compare
227bbf6 to
fe18a99
Compare
ed19082 to
d5a9a3f
Compare
fe18a99 to
2ecdc39
Compare
c356f95 to
d0d8bed
Compare
2ecdc39 to
6ca3b6b
Compare
d0d8bed to
61f38e1
Compare
510cdf7 to
b990700
Compare
7ea9b07 to
ea8da6c
Compare
b990700 to
4188667
Compare
ea8da6c to
b87689a
Compare
4188667 to
6e31502
Compare
b87689a to
c7bb834
Compare
22fa0a7 to
1e6bde2
Compare
Coverage report
Test suite run success3888 tests passing in 1494 suites. Report generated by 🧪jest coverage report action from fceb299 |
1e6bde2 to
1a0a929
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/common/collection.d.ts@@ -8,7 +8,9 @@ import type { List, ValueIteratee } from 'lodash';
* @param iteratee - The function invoked per iteration.
* @returns Returns the composed aggregate object.
*/
-export declare function groupBy<T>(collection: ArrayLike<T> | null | undefined, iteratee?: ValueIteratee<T>): Record<string, T[]>;
+export declare function groupBy<T>(collection: ArrayLike<T> | null | undefined, iteratee?: ValueIteratee<T>): {
+ [index: string]: T[];
+};
/**
* Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for,
* while the second of which contains elements predicate returns falsey for.
packages/cli-kit/dist/private/node/conf-store.d.ts@@ -123,10 +123,12 @@ interface RunWithRateLimitOptions {
* @returns true, or undefined if the task was not run.
*/
export declare function runWithRateLimit(options: RunWithRateLimitOptions, config?: LocalStorage<ConfSchema>): Promise<boolean>;
-export declare function getConfigStoreForPartnerStatus(): LocalStorage<Record<string, {
- status: true;
- checkedAt: string;
-}>>;
+export declare function getConfigStoreForPartnerStatus(): LocalStorage<{
+ [partnerToken: string]: {
+ status: true;
+ checkedAt: string;
+ };
+}>;
export declare function getCachedPartnerAccountStatus(partnersToken: string): true | null;
export declare function setCachedPartnerAccountStatus(partnersToken: string): void;
export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/dot-env.d.ts@@ -9,7 +9,9 @@ export interface DotEnvFile {
/**
* Variables of the .env file.
*/
- variables: Record<string, string>;
+ variables: {
+ [name: string]: string;
+ };
}
/**
* Reads and parses a .env file.
@@ -28,5 +30,7 @@ export declare function writeDotEnv(file: DotEnvFile): Promise<void>;
* @param envFileContent - .env file contents.
* @param updatedValues - object containing new env variables values.
*/
-export declare function patchEnvFile(envFileContent: string | null, updatedValues: Record<string, string | undefined>): string;
+export declare function patchEnvFile(envFileContent: string | null, updatedValues: {
+ [key: string]: string | undefined;
+}): string;
export declare function createDotEnvFileLine(key: string, value?: string, quote?: string): string;
\ No newline at end of file
packages/cli-kit/dist/public/node/environments.d.ts@@ -1,5 +1,7 @@
import { JsonMap } from '../../private/common/json.js';
-export type Environments = Record<string, JsonMap>;
+export interface Environments {
+ [name: string]: JsonMap;
+}
interface LoadEnvironmentOptions {
from?: string;
silent?: boolean;
packages/cli-kit/dist/public/node/error.d.ts@@ -1,5 +1,5 @@
import { AlertCustomSection } from './ui.js';
-import { OutputMessage } from './output.js';
+import { OutputMessage } from '../../public/node/output.js';
import { InlineToken, TokenItem } from '../../private/node/ui/components/TokenizedText.js';
export { ExtendableError } from 'ts-error';
export declare enum FatalErrorType {
packages/cli-kit/dist/public/node/fs.d.ts@@ -1,5 +1,5 @@
-import { OverloadParameters } from '../../private/common/ts/overloaded-parameters.js';
import { RandomNameFamily } from '../common/string.js';
+import { OverloadParameters } from '../../private/common/ts/overloaded-parameters.js';
import { findUp as internalFindUp } from 'find-up';
import { ReadStream, WriteStream } from 'fs';
import type { Pattern, Options as GlobOptions } from 'fast-glob';
packages/cli-kit/dist/public/node/git.d.ts@@ -17,7 +17,9 @@ export declare function initializeGitRepository(directory: string, initialBranch
* @returns Files ignored by the lockfile.
*/
export declare function checkIfIgnoredInGitRepository(directory: string, files: string[]): Promise<string[]>;
-export type GitIgnoreTemplate = Record<string, string[]>;
+export interface GitIgnoreTemplate {
+ [section: string]: string[];
+}
/**
* Create a .gitignore file in the given directory.
*
packages/cli-kit/dist/public/node/json-schema.d.ts@@ -1,7 +1,9 @@
import { ParseConfigurationResult } from './schema.js';
import { ErrorObject, SchemaObject } from 'ajv';
export type HandleInvalidAdditionalProperties = 'strip' | 'fail';
-type AjvError = ErrorObject<string, Record<string, unknown>>;
+type AjvError = ErrorObject<string, {
+ [key: string]: unknown;
+}>;
/**
* Normalises a JSON Schema by standardising it's internal implementation.
*
packages/cli-kit/dist/public/node/local-storage.d.ts@@ -2,7 +2,9 @@
* A wrapper around the package that provides a strongly-typed interface
* for accessing the local storage.
*/
-export declare class LocalStorage<T extends Record<string, any>> {
+export declare class LocalStorage<T extends {
+ [key: string]: any;
+}> {
private readonly config;
constructor(options: {
projectName?: string;
packages/cli-kit/dist/public/node/metadata.d.ts@@ -33,7 +33,9 @@ export type SensitiveSchema<T> = T extends RuntimeMetadataManager<infer _TPublic
* @param defaultPublicMetadata - Optional, default data for the container.
* @returns A container for the metadata.
*/
-export declare function createRuntimeMetadataContainer<TPublic extends AnyJson, TSensitive extends AnyJson = Record<string, never>>(defaultPublicMetadata?: Partial<TPublic>): RuntimeMetadataManager<TPublic, TSensitive>;
+export declare function createRuntimeMetadataContainer<TPublic extends AnyJson, TSensitive extends AnyJson = {
+ [key: string]: never;
+}>(defaultPublicMetadata?: Partial<TPublic>): RuntimeMetadataManager<TPublic, TSensitive>;
type CmdFieldsFromMonorail = PickByPrefix<MonorailEventPublic, 'cmd_all_'> & PickByPrefix<MonorailEventPublic, 'cmd_app_'> & PickByPrefix<MonorailEventPublic, 'cmd_create_app_'> & PickByPrefix<MonorailEventPublic, 'cmd_theme_'> & PickByPrefix<MonorailEventPublic, 'store_'>;
declare const coreData: RuntimeMetadataManager<CmdFieldsFromMonorail, {
commandStartOptions: {
packages/cli-kit/dist/public/node/mimes.d.ts@@ -10,4 +10,6 @@ export declare function lookupMimeType(fileName: string): string;
*
* @param newTypes - Object of key-values where key is extension and value is mime type.
*/
-export declare function setMimeTypes(newTypes: Record<string, string>): void;
\ No newline at end of file
+export declare function setMimeTypes(newTypes: {
+ [key: string]: string;
+}): void;
\ No newline at end of file
packages/cli-kit/dist/public/node/multiple-installation-warning.d.ts@@ -5,4 +5,6 @@
* @param directory - The directory of the project.
* @param dependencies - The dependencies of the project.
*/
-export declare function showMultipleCLIWarningIfNeeded(directory: string, dependencies: Record<string, string>): Promise<void>;
\ No newline at end of file
+export declare function showMultipleCLIWarningIfNeeded(directory: string, dependencies: {
+ [key: string]: string;
+}): Promise<void>;
\ No newline at end of file
packages/cli-kit/dist/public/node/node-package-manager.d.ts@@ -13,7 +13,9 @@ export declare const bunLockfile = "bun.lockb";
export declare const pnpmWorkspaceFile = "pnpm-workspace.yaml";
/** An array containing the lockfiles from all the package managers */
export declare const lockfiles: Lockfile[];
-export declare const lockfilesByManager: Record<PackageManager, Lockfile | undefined>;
+export declare const lockfilesByManager: {
+ [key in PackageManager]: Lockfile | undefined;
+};
export type Lockfile = 'yarn.lock' | 'package-lock.json' | 'pnpm-lock.yaml' | 'bun.lockb';
/**
* A union type that represents the type of dependencies in the package.json
@@ -111,7 +113,9 @@ export declare function getPackageVersion(packageJsonPath: string): Promise<stri
* @param packageJsonPath - Path to the package.json file
* @returns A promise that resolves with the list of dependencies.
*/
-export declare function getDependencies(packageJsonPath: string): Promise<Record<string, string>>;
+export declare function getDependencies(packageJsonPath: string): Promise<{
+ [key: string]: string;
+}>;
/**
* Returns true if the app uses workspaces, false otherwise.
* @param packageJsonPath - Path to the package.json file
@@ -163,19 +167,27 @@ export interface PackageJson {
/**
* The scripts attribute of the package.json
*/
- scripts?: Record<string, string>;
+ scripts?: {
+ [key: string]: string;
+ };
/**
* The dependencies attribute of the package.json
*/
- dependencies?: Record<string, string>;
+ dependencies?: {
+ [key: string]: string;
+ };
/**
* The devDependencies attribute of the package.json
*/
- devDependencies?: Record<string, string>;
+ devDependencies?: {
+ [key: string]: string;
+ };
/**
* The peerDependencies attribute of the package.json
*/
- peerDependencies?: Record<string, string>;
+ peerDependencies?: {
+ [key: string]: string;
+ };
/**
* The optional oclif settings attribute of the package.json
*/
@@ -189,11 +201,15 @@ export interface PackageJson {
/**
* The resolutions attribute of the package.json. Only useful when using yarn as package manager
*/
- resolutions?: Record<string, string>;
+ resolutions?: {
+ [key: string]: string;
+ };
/**
* The overrides attribute of the package.json. Only useful when using npm o npmn as package managers
*/
- overrides?: Record<string, string>;
+ overrides?: {
+ [key: string]: string;
+ };
/**
* The prettier attribute of the package.json
*/
@@ -268,7 +284,9 @@ export declare function findUpAndReadPackageJson(fromDirectory: string): Promise
path: string;
content: PackageJson;
}>;
-export declare function addResolutionOrOverride(directory: string, dependencies: Record<string, string>): Promise<void>;
+export declare function addResolutionOrOverride(directory: string, dependencies: {
+ [key: string]: string;
+}): Promise<void>;
/**
* Writes the package.json file to the given directory.
*
packages/cli-kit/dist/public/node/output.d.ts@@ -49,7 +49,9 @@ export declare function formatPackageManagerCommand(packageManager: PackageManag
export declare function outputContent(strings: TemplateStringsArray, ...keys: (ContentToken<unknown> | string)[]): TokenizedString;
/** Log levels. */
export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'silent';
-export declare let collectedLogs: Record<string, string[]>;
+export declare let collectedLogs: {
+ [key: string]: string[];
+};
/**
* This is only used during UnitTesting.
* If we are in a testing context, instead of printing the logs to the console,
packages/cli-kit/dist/public/node/plugins.d.ts@@ -20,22 +20,30 @@ type AppSpecificMonorailFields = PickByPrefix<MonorailEventPublic, 'app_', 'proj
type AppSpecificSensitiveMonorailFields = PickByPrefix<MonorailEventSensitive, 'app_'>;
export interface HookReturnsPerPlugin extends HookReturnPerTunnelPlugin {
public_command_metadata: {
- options: Record<string, never>;
+ options: {
+ [key: string]: never;
+ };
pluginReturns: {
'@shopify/app': Partial<AppSpecificMonorailFields>;
[pluginName: string]: JsonMap;
};
};
sensitive_command_metadata: {
- options: Record<string, never>;
+ options: {
+ [key: string]: never;
+ };
pluginReturns: {
'@shopify/app': Partial<AppSpecificSensitiveMonorailFields>;
[pluginName: string]: JsonMap;
};
};
[hookName: string]: {
- options: Record<string, unknown>;
- pluginReturns: Record<string, unknown>;
+ options: {
+ [key: string]: unknown;
+ };
+ pluginReturns: {
+ [key: string]: unknown;
+ };
};
}
export type PluginReturnsForHook<TEvent extends keyof TPluginMap, TPluginName extends keyof TPluginMap[TEvent]['pluginReturns'], TPluginMap extends HookReturnsPerPlugin = HookReturnsPerPlugin> = TPluginMap[TEvent]['pluginReturns'][TPluginName];
packages/cli-kit/dist/public/node/system.d.ts@@ -2,7 +2,9 @@ import { AbortSignal } from './abort.js';
import type { Writable, Readable } from 'stream';
export interface ExecOptions {
cwd?: string;
- env?: Record<string, string | undefined>;
+ env?: {
+ [key: string]: string | undefined;
+ };
stdin?: Readable | 'inherit';
stdout?: Writable | 'inherit';
stderr?: Writable | 'inherit';
packages/cli-kit/dist/public/common/ts/json-narrowing.d.ts@@ -4,4 +4,6 @@
* @param unknownBlob - The unknown object to validate.
* @throws BugError - Thrown if the unknownBlob is not a string map.
*/
-export declare function assertStringMap(unknownBlob: unknown): asserts unknownBlob is Record<string, string>;
\ No newline at end of file
+export declare function assertStringMap(unknownBlob: unknown): asserts unknownBlob is {
+ [key: string]: string;
+};
\ No newline at end of file
packages/cli-kit/dist/private/node/analytics/bounded-collections.d.ts@@ -38,6 +38,8 @@ export declare class BMap<TKey, TValue> extends Map<TKey, TValue> {
set(key: TKey, value: TValue): this;
delete(key: TKey): boolean;
clear(): void;
- toObject(): Record<string, TValue>;
+ toObject(): {
+ [key: string]: TValue;
+ };
private enforceLimit;
}
\ No newline at end of file
packages/cli-kit/dist/private/node/api/graphql.d.ts@@ -1,4 +1,6 @@
import { Variables } from 'graphql-request';
-export declare function debugLogRequestInfo(api: string, query: string, url: string, variables?: Variables, headers?: Record<string, string>): void;
+export declare function debugLogRequestInfo(api: string, query: string, url: string, variables?: Variables, headers?: {
+ [key: string]: string;
+}): void;
export declare function sanitizeVariables(variables: Variables): string;
export declare function errorHandler(api: string): (error: unknown, requestId?: string) => unknown;
\ No newline at end of file
packages/cli-kit/dist/private/node/api/headers.d.ts@@ -13,8 +13,12 @@ export declare class GraphQLClientError extends RequestClientError {
* @param headers - HTTP headers.
* @returns A sanitized version of the headers as a string.
*/
-export declare function sanitizedHeadersOutput(headers: Record<string, string>): string;
-export declare function buildHeaders(token?: string): Record<string, string>;
+export declare function sanitizedHeadersOutput(headers: {
+ [key: string]: string;
+}): string;
+export declare function buildHeaders(token?: string): {
+ [key: string]: string;
+};
/**
* This utility function returns the https.Agent to use for a given service.
*/
packages/cli-kit/dist/private/node/api/rest.d.ts@@ -1,5 +1,9 @@
import { AdminSession } from '../../../public/node/session.js';
export declare function restRequestBody<T>(requestBody?: T): string | undefined;
-export declare function restRequestUrl(session: AdminSession, apiVersion: string, path: string, searchParams?: Record<string, string>): string;
-export declare function restRequestHeaders(session: AdminSession): Record<string, string>;
+export declare function restRequestUrl(session: AdminSession, apiVersion: string, path: string, searchParams?: {
+ [name: string]: string;
+}): string;
+export declare function restRequestHeaders(session: AdminSession): {
+ [key: string]: string;
+};
export declare function isThemeAccessSession(session: AdminSession): boolean;
\ No newline at end of file
packages/cli-kit/dist/private/node/session/exchange.d.ts@@ -19,7 +19,9 @@ export interface ExchangeScopes {
* @param store - the store to use, only needed for admin API
* @returns An array with the application access tokens.
*/
-export declare function exchangeAccessForApplicationTokens(identityToken: IdentityToken, scopes: ExchangeScopes, store?: string): Promise<Record<string, ApplicationToken>>;
+export declare function exchangeAccessForApplicationTokens(identityToken: IdentityToken, scopes: ExchangeScopes, store?: string): Promise<{
+ [x: string]: ApplicationToken;
+}>;
/**
* Given an expired access token, refresh it to get a new one.
*/
@@ -60,5 +62,7 @@ type IdentityDeviceError = 'authorization_pending' | 'access_denied' | 'expired_
* @returns An instance with the identity access tokens.
*/
export declare function exchangeDeviceCodeForAccessToken(deviceCode: string): Promise<Result<IdentityToken, IdentityDeviceError>>;
-export declare function requestAppToken(api: API, token: string, scopes?: string[], store?: string): Promise<Record<string, ApplicationToken>>;
+export declare function requestAppToken(api: API, token: string, scopes?: string[], store?: string): Promise<{
+ [x: string]: ApplicationToken;
+}>;
export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/api/admin.d.ts@@ -73,7 +73,9 @@ interface ApiVersion {
* @param apiVersion - Admin API version.
* @returns - The {@link RestResponse}.
*/
-export declare function restRequest<T>(method: string, path: string, session: AdminSession, requestBody?: T, searchParams?: Record<string, string>, apiVersion?: string): Promise<RestResponse>;
+export declare function restRequest<T>(method: string, path: string, session: AdminSession, requestBody?: T, searchParams?: {
+ [name: string]: string;
+}, apiVersion?: string): Promise<RestResponse>;
/**
* Respose of a REST request.
*/
@@ -89,6 +91,8 @@ export interface RestResponse {
/**
* HTTP response headers.
*/
- headers: Record<string, string[]>;
+ headers: {
+ [key: string]: string[];
+ };
}
export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/api/app-dev.d.ts@@ -1,5 +1,5 @@
-import { RequestOptions } from './app-management.js';
import { UnauthorizedHandler } from './graphql.js';
+import { RequestOptions } from './app-management.js';
import { Variables } from 'graphql-request';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
/**
packages/cli-kit/dist/public/node/api/app-management.d.ts@@ -2,7 +2,9 @@ import { CacheOptions, GraphQLResponse, UnauthorizedHandler } from './graphql.js
import { RequestModeInput } from '../http.js';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
import { Variables } from 'graphql-request';
-export declare const appManagementHeaders: (token: string) => Record<string, string>;
+export declare const appManagementHeaders: (token: string) => {
+ [key: string]: string;
+};
export declare const appManagementAppLogsUrl: (organizationId: string, cursor?: string, filters?: {
status?: string;
source?: string;
packages/cli-kit/dist/public/node/api/graphql.d.ts@@ -3,10 +3,14 @@ import { LocalStorage } from '../local-storage.js';
import { RequestModeInput } from '../http.js';
import { rawRequest, RequestDocument, Variables } from 'graphql-request';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
-export type Exact<T extends Record<string, unknown>> = {
+export type Exact<T extends {
+ [key: string]: unknown;
+}> = {
[K in keyof T]: T[K];
};
-export type GraphQLVariables = Record<string, any>;
+export interface GraphQLVariables {
+ [key: string]: any;
+}
export type GraphQLResponse<T> = Awaited<ReturnType<typeof rawRequest<T>>>;
export interface CacheOptions {
cacheTTL: TimeInterval;
@@ -25,7 +29,9 @@ interface GraphQLRequestBaseOptions<TResult> {
api: string;
url: string;
token?: string;
- addedHeaders?: Record<string, string>;
+ addedHeaders?: {
+ [header: string]: string;
+ };
responseOptions?: GraphQLResponseOptions<TResult>;
cacheOptions?: CacheOptions;
preferredBehaviour?: RequestModeInput;
@@ -36,7 +42,9 @@ export type GraphQLRequestOptions<T> = GraphQLRequestBaseOptions<T> & {
unauthorizedHandler?: UnauthorizedHandler;
};
export type GraphQLRequestDocOptions<TResult, TVariables> = GraphQLRequestBaseOptions<TResult> & {
- query: TypedDocumentNode<TResult, TVariables> | TypedDocumentNode<TResult, Exact<Record<string, never>>>;
+ query: TypedDocumentNode<TResult, TVariables> | TypedDocumentNode<TResult, Exact<{
+ [key: string]: never;
+ }>>;
variables?: TVariables;
unauthorizedHandler?: UnauthorizedHandler;
autoRateLimitRestore?: boolean;
packages/cli-kit/dist/public/node/doctor/framework.d.ts@@ -3,23 +3,23 @@ import type { DoctorContext, TestResult } from './types.js';
* Result from running a CLI command.
*/
interface CommandResult {
- /** The full command that was run. */
+ /** The full command that was run */
command: string;
- /** Exit code (0 = success). */
+ /** Exit code (0 = success) */
exitCode: number;
- /** Standard output. */
+ /** Standard output */
stdout: string;
- /** Standard error. */
+ /** Standard error */
stderr: string;
- /** Combined output (stdout + stderr). */
+ /** Combined output (stdout + stderr) */
output: string;
- /** Whether the command succeeded (exitCode === 0). */
+ /** Whether the command succeeded (exitCode === 0) */
success: boolean;
}
/**
* Base class for doctor test suites.
*
- * Write tests using the test() method.
+ * Write tests using the test() method:.
*
*
*/
@@ -32,7 +32,6 @@ export declare abstract class DoctorSuite<TContext extends DoctorContext = Docto
* Run the entire test suite.
*
* @param context - The doctor context for this suite run.
- * @returns The list of test results.
*/
runSuite(context: TContext): Promise<TestResult[]>;
/**
@@ -50,9 +49,7 @@ export declare abstract class DoctorSuite<TContext extends DoctorContext = Docto
* Run a CLI command and return the result.
*
* @param command - The CLI command to run.
- * @param options - Optional overrides.
- * @param options.cwd - Working directory for the command.
- * @param options.env - Environment variables for the command.
+ * @param options - Optional cwd and env overrides.
* @example
* const result = await this.run('shopify theme init my-theme')
* const result = await this.run('shopify theme push --json')
@@ -68,9 +65,7 @@ export declare abstract class DoctorSuite<TContext extends DoctorContext = Docto
* Returns only success/failure.
*
* @param command - The CLI command to run.
- * @param options - Optional overrides.
- * @param options.cwd - Working directory for the command.
- * @param options.env - Environment variables for the command.
+ * @param options - Optional cwd and env overrides.
*/
protected runInteractive(command: string, options?: {
cwd?: string;
packages/cli-kit/dist/public/node/doctor/reporter.d.ts@@ -2,32 +2,9 @@ import type { TestResult } from './types.js';
/**
* Initialize the reporter with a base path for truncating file paths in output.
* Call this before running tests to enable path truncation.
- *
- * @param basePath - The base path used to truncate absolute paths in output.
*/
export declare function initReporter(basePath: string): void;
-/**
- * Log the start of a test suite.
- *
- * @param suiteName - The name of the suite.
- * @param description - The suite description.
- */
export declare function reportSuiteStart(suiteName: string, description: string): void;
-/**
- * Log the start of a test.
- *
- * @param testName - The name of the test.
- */
export declare function reportTestStart(testName: string): void;
-/**
- * Log the result of a single test (passed, failed, or skipped).
- *
- * @param result - The test result to report.
- */
export declare function reportTestResult(result: TestResult): void;
-/**
- * Log a summary of all test results.
- *
- * @param results - The list of test results to summarize.
- */
export declare function reportSummary(results: TestResult[]): void;
\ No newline at end of file
packages/cli-kit/dist/public/node/plugins/tunnel.d.ts@@ -37,13 +37,19 @@ export interface HookReturnPerTunnelPlugin {
port: number;
provider: string;
};
- pluginReturns: Record<string, Result<TunnelClient, TunnelError>>;
+ pluginReturns: {
+ [key: string]: Result<TunnelClient, TunnelError>;
+ };
};
tunnel_provider: {
- options: Record<string, never>;
- pluginReturns: Record<string, {
- name: string;
- }>;
+ options: {
+ [key: string]: never;
+ };
+ pluginReturns: {
+ [pluginName: string]: {
+ name: string;
+ };
+ };
};
}
export type TunnelProviderFunction = FanoutHookFunction<'tunnel_provider', ''>;
packages/cli-kit/dist/public/node/themes/conf.d.ts@@ -2,7 +2,9 @@ import { LocalStorage } from '../local-storage.js';
import { AdminSession } from '../session.js';
type HostThemeId = string;
type StoreFqdn = AdminSession['storeFqdn'];
-type HostThemeLocalStorageSchema = Record<StoreFqdn, HostThemeId>;
+interface HostThemeLocalStorageSchema {
+ [themeStore: StoreFqdn]: HostThemeId;
+}
export declare function hostThemeLocalStorage(): LocalStorage<HostThemeLocalStorageSchema>;
export declare function getHostTheme(storeFqdn: StoreFqdn): string | undefined;
export declare function setHostTheme(storeFqdn: StoreFqdn, themeId: HostThemeId): void;
packages/cli-kit/dist/private/node/ui/contexts/LinksContext.d.ts@@ -4,7 +4,9 @@ export interface Link {
url: string;
}
export interface ContextValue {
- links: React.RefObject<Record<string, Link>>;
+ links: React.RefObject<{
+ [key: string]: Link;
+ }>;
addLink: (label: string | undefined, url: string) => string;
}
export declare const LinksContext: React.Context<ContextValue | null>;
\ No newline at end of file
packages/cli-kit/dist/private/node/ui/components/SelectInput.d.ts@@ -1,7 +1,7 @@
import React from 'react';
import { DOMElement } from 'ink';
declare module 'react' {
- function forwardRef<T, TProps>(render: (props: TProps, ref: React.Ref<T>) => React.ReactElement | null): (props: TProps & React.RefAttributes<T>) => React.ReactElement | null;
+ function forwardRef<T, P>(render: (props: P, ref: React.Ref<T>) => React.ReactElement | null): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}
export interface SelectInputProps<T> {
items: Item<T>[];
packages/cli-kit/dist/public/node/vendor/dev_server/dev-server-2016.d.ts@@ -1,17 +1,9 @@
import { HostOptions } from './types.js';
-/**
- *
- * @param projectName
- */
export declare function createServer(projectName: string): {
host: (options?: HostOptions) => string;
url: (options?: HostOptions) => string;
};
declare function assertRunning2016(projectName: string): void;
declare let assertRunningOverride: typeof assertRunning2016 | undefined;
-/**
- *
- * @param override
- */
export declare function setAssertRunning(override: typeof assertRunningOverride): void;
export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/dev_server/dev-server-2024.d.ts@@ -1,17 +1,9 @@
import type { HostOptions } from './types.js';
-/**
- *
- * @param projectName
- */
export declare function createServer(projectName: string): {
host: (options?: HostOptions) => string;
url: (options?: HostOptions) => string;
};
declare function assertRunning2024(projectName: string): void;
declare let assertRunningOverride: typeof assertRunning2024 | undefined;
-/**
- *
- * @param override
- */
export declare function setAssertRunning(override: typeof assertRunningOverride): void;
export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/dev_server/env.d.ts@@ -1,5 +1,2 @@
export declare const isDevServerEnvironment: boolean;
-/**
- *
- */
export declare function assertCompatibleEnvironment(): void;
\ No newline at end of file
packages/cli-kit/dist/private/node/ui/components/Prompts/InfoTable.d.ts@@ -12,7 +12,9 @@ export interface InfoTableSection {
emptyItemsText?: string;
}
export interface InfoTableProps {
- table: Record<string, Items> | InfoTableSection[];
+ table: {
+ [header: string]: Items;
+ } | InfoTableSection[];
}
declare const InfoTable: FunctionComponent<InfoTableProps>;
export { InfoTable };
\ No newline at end of file
packages/cli-kit/dist/private/node/ui/components/Prompts/PromptLayout.d.ts@@ -1,5 +1,5 @@
-import { InfoTableProps } from './InfoTable.js';
import { InfoMessageProps } from './InfoMessage.js';
+import { InfoTableProps } from './InfoTable.js';
import { InlineToken, LinkToken, TokenItem } from '../TokenizedText.js';
import { AbortSignal } from '../../../../../public/node/abort.js';
import { PromptState } from '../../hooks/use-prompt.js';
packages/cli-kit/dist/private/node/ui/components/Table/ScalarDict.d.ts@@ -1,3 +1,5 @@
type Scalar = string | number | boolean | null | undefined;
-type ScalarDict = Record<string, Scalar>;
-export default ScalarDict;
\ No newline at end of file
+export default interface ScalarDict {
+ [key: string]: Scalar;
+}
+export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/dev_server/network/host.d.ts@@ -1,9 +1,2 @@
-/**
- *
- * @param hostname
- */
export declare function getIpFromHosts(hostname: string): string;
-/**
- *
- */
export declare function TEST_ClearCache(): void;
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/dev_server/network/index.d.ts@@ -5,12 +5,5 @@ export interface ConnectionArguments {
port: number;
timeout?: number;
}
-/**
- *
- * @param options
- */
export declare function assertConnectable(options: ConnectionArguments): void;
-/**
- *
- */
export declare function TEST_testResetCheckPort(): void;
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/otel-js/export/InstantaneousMetricReader.d.ts@@ -1,5 +1,5 @@
-import { MetricReader } from '@opentelemetry/sdk-metrics';
import type { PushMetricExporter } from '@opentelemetry/sdk-metrics';
+import { MetricReader } from '@opentelemetry/sdk-metrics';
export interface InstantaneousMetricReaderOptions {
/**
* The backing exporter for the metric reader.
packages/cli-kit/dist/public/node/vendor/otel-js/service/types.d.ts@@ -1,11 +1,13 @@
import type { Counter, Histogram, MeterProvider, MetricAttributes, MetricOptions, UpDownCounter } from '@opentelemetry/api';
import type { ViewOptions } from '@opentelemetry/sdk-metrics';
-export type CustomMetricLabels<TLabels extends Record<TKeys, MetricAttributes>, TKeys extends string = keyof TLabels & string> = {
+export type CustomMetricLabels<TLabels extends {
+ [key in TKeys]: MetricAttributes;
+}, TKeys extends string = keyof TLabels & string> = {
[P in TKeys]: TLabels[P] extends MetricAttributes ? TLabels[P] : never;
};
-export type MetricRecording<TAttributes extends MetricAttributes = MetricAttributes> = [value: number, labels?: TAttributes];
-export type RecordMetricFunction<TAttributes extends MetricAttributes = MetricAttributes> = (...args: MetricRecording<TAttributes>) => void;
-export type OnRecordCallback<TAttributes extends MetricAttributes = MetricAttributes> = (metricName: string, ...args: MetricRecording<TAttributes>) => MetricRecording<TAttributes> | void;
+export type MetricRecording<TAttributes extends MetricAttributes = any> = [value: number, labels?: TAttributes];
+export type RecordMetricFunction<TAttributes extends MetricAttributes = any> = (...args: MetricRecording<TAttributes>) => void;
+export type OnRecordCallback<TAttributes extends MetricAttributes = any> = (metricName: string, ...args: MetricRecording<TAttributes>) => MetricRecording<TAttributes> | void;
export type MetricInstrument = Histogram | Counter | UpDownCounter;
export declare enum MetricInstrumentType {
Histogram = "Histogram",
@@ -21,12 +23,14 @@ export type MetricDescriptor = MetricOptions & ({
} | {
type: MetricInstrumentType.Counter | MetricInstrumentType.UpDownCounter;
});
-export type MetricsConfig = Record<string, MetricDescriptor>;
+export interface MetricsConfig {
+ [key: string]: MetricDescriptor;
+}
export interface OtelService {
readonly serviceName: string;
getMeterProvider(): MeterProvider;
addView(viewOptions: ViewOptions): void;
- record<TAttributes extends MetricAttributes = MetricAttributes>(...args: Parameters<OnRecordCallback<TAttributes>>): void;
+ record<TAttributes extends MetricAttributes = any>(...args: Parameters<OnRecordCallback<TAttributes>>): void;
/**
* callback is called when a metric is recorded.
* Returns a function to unsubscribe.
packages/cli-kit/dist/public/node/vendor/otel-js/utils/throttle.d.ts@@ -1,15 +1,7 @@
-type ThrottledFunction<T extends (...args: unknown[]) => unknown> = (...args: Parameters<T>) => ReturnType<T>;
+type ThrottledFunction<T extends (...args: any) => any> = (...args: Parameters<T>) => ReturnType<T>;
interface ThrottleOptions {
leading?: boolean;
trailing?: boolean;
}
-/**
- *
- * @param func
- * @param wait
- * @param root0
- * @param root0.leading
- * @param root0.trailing
- */
-export declare function throttle<T extends (...args: unknown[]) => unknown>(func: T, wait: number, { leading, trailing }?: ThrottleOptions): ThrottledFunction<T>;
+export declare function throttle<T extends (...args: any) => any>(func: T, wait: number, { leading, trailing }?: ThrottleOptions): ThrottledFunction<T>;
export {};
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/otel-js/utils/validators.d.ts@@ -1,5 +1 @@
-/**
- *
- * @param value
- */
export declare function isValidMetricName(value: string): boolean;
\ No newline at end of file
packages/cli-kit/dist/public/node/vendor/otel-js/service/BaseOtelService/BaseOtelService.d.ts@@ -32,13 +32,6 @@ export declare class BaseOtelService implements OtelService {
protected readonly recordListeners: Set<OnRecordCallback>;
/**
* Bootstraps an Otel exporter which can send Otel metrics to a dedicated Shopify supported collector endpoint.
- *
- * @param root0
- * @param root0.serviceName
- * @param root0.prefixMetric
- * @param root0.metrics
- * @param root0.onRecord
- * @param root0.meterProvider
*/
constructor({ serviceName, prefixMetric, metrics, onRecord, meterProvider }: BaseOtelServiceOptions);
getMeterProvider(): MeterProvider;
packages/cli-kit/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultOtelService.d.ts@@ -2,7 +2,7 @@ import { BaseOtelService } from '../BaseOtelService/BaseOtelService.js';
import type { BaseOtelServiceOptions } from '../BaseOtelService/BaseOtelService.js';
export interface DefaultOtelServiceOptions extends BaseOtelServiceOptions {
/**
- * What environment is being deployed (production, staging).
+ * What environment is being deployed (production, staging)
*/
env?: string;
/**
@@ -18,17 +18,6 @@ export interface DefaultOtelServiceOptions extends BaseOtelServiceOptions {
export declare class DefaultOtelService extends BaseOtelService {
/**
* Bootstraps an Otel exporter which can send Otel metrics to a dedicated Shopify supported collector endpoint.
- *
- * @param root0
- * @param root0.throttleLimit
- * @param root0.env
- * @param root0.serviceName
- * @param root0.prefixMetric
- * @param root0.metrics
- * @param root0.onRecord
- * @param root0.meterProvider
- * @param root0.useXhr
- * @param root0.otelEndpoint
*/
constructor({ throttleLimit, env, serviceName, prefixMetric, metrics, onRecord, meterProvider, useXhr, otelEndpoint, }: DefaultOtelServiceOptions);
shutdown(): Promise<void>;
|
1a0a929 to
fe6ef05
Compare
c7bb834 to
f042eff
Compare
dfbb35a to
1abe726
Compare
93493c9 to
af69f6c
Compare
2a98fca to
fe9bf87
Compare
af69f6c to
4061625
Compare
|
We detected some changes at Caution DO NOT create changesets for features which you do not wish to be included in the public changelog of the next CLI release. |
fe9bf87 to
0a81281
Compare
4061625 to
5bbfb2b
Compare
|
🤖 Code Review · #projects-dev-ai for questions ✅ Complete - 1 findings 📋 History❌ Failed → ✅ 3 findings → ✅ 2 findings → ✅ No issues → ✅ No issues → ✅ 1 findings → ✅ No issues → ✅ 1 findings → ✅ No issues → ✅ No issues → ✅ 1 findings |
0a81281 to
ca0c4f5
Compare
| return undefined | ||
| } | ||
| const output = stepResult.output as BuildManifestStepOutput | ||
| const staticAssets = collectStaticAssets(output) |
There was a problem hiding this comment.
Unsafe/unvalidated cast of prior step output (manifest_result) may crash or copy wrong files
copyStaticAssetsFromManifest casts context.stepResults.get(stepId).output to BuildManifestStepOutput without runtime validation. If output is undefined, has a different shape, or manifests entries don’t contain build_manifest.assets, this can throw at runtime and fail builds. If the shape partially matches but contains unexpected values, it may attempt file copies using attacker-controlled filepath/module fields from an untrusted step output.
| const item = raw as {[key: string]: unknown} | ||
| const keyByValue = String(getNestedValue(item, keyBy) ?? '') | ||
| const assets = resolveAssets(config.assets, extensionConfig, item, keyByValue, options.stdout) | ||
| return [{[keyBy]: getNestedValue(item, keyBy), build_manifest: {assets}} as PerItemManifest] |
There was a problem hiding this comment.
forEach keyBy missing/empty becomes '' causing ambiguous filenames and collisions
In forEach mode, const keyByValue = String(getNestedValue(item, keyBy) ?? '') turns missing/null keyBy values into an empty string. This produces filenames with double -- and allows multiple items to collide/overwrite outputs. It also emits {[keyBy]: undefined} which makes downstream mapping unreliable.
| const sourcePath = joinPath(context.extension.directory, asset.module) | ||
| const destPath = joinPath(outputDir, asset.filepath) | ||
| await mkdir(dirname(destPath)) | ||
| await copyFile(sourcePath, destPath) |
There was a problem hiding this comment.
Potential path traversal/unintended file inclusion when copying asset.module
When copying assets from manifest_result, code joins context.extension.directory with asset.module and outputDir with asset.filepath. If asset.module contains ../ or absolute paths (depending on join semantics), files outside the extension directory could be read and packaged (e.g., ../../.env). Similarly, asset.filepath could escape the output directory.
|
|
||
| context.options.stdout.write(`Copied ${staticAssets.length} static asset(s) from build manifest\n`) | ||
| return staticAssets.length | ||
| } |
There was a problem hiding this comment.
manifest_result reports incorrect filesCopied when static assets lack module
copyStaticAssetsFromManifest returns staticAssets.length, but it intentionally skips copying assets that don’t have asset.module (“not file-copy candidates”). This causes the step to report that it copied files it didn’t copy. Downstream logic/CI assertions may rely on filesCopied, and logs/telemetry become misleading. Evidence: the function returns staticAssets.length even though it may return early inside the map callback:
if (!asset.module) return
...
return staticAssets.lengthThe added test (“counted but skipped in copy”) confirms the mismatch is currently accepted by tests but remains misleading in production.
| 'assets' in output | ||
| ? output.assets | ||
| : output.manifests.reduce<ResolvedAssets>((acc, manifest) => ({...acc, ...manifest.build_manifest.assets}), {}) | ||
|
|
There was a problem hiding this comment.
forEach manifest_result can drop static assets due to asset-key collisions during merge
collectStaticAssets flattens forEach manifests by merging all per-item assets maps into a single object using object spread:
output.manifests.reduce<ResolvedAssets>((acc, manifest) => ({...acc, ...manifest.build_manifest.assets}), {})This is lossy whenever different manifests share the same asset keys (common across targets: tools, icon, etc.). Later entries overwrite earlier ones, so static assets from earlier targets can be skipped. This breaks the “copy static assets across all targets” behavior and can cause target-specific assets (e.g., different tools.json) to be omitted silently.
| return executeBuildFunctionStep(step, context) | ||
|
|
||
| case 'create_tax_stub': | ||
| return executeCreateTaxStubStep(step, context) |
There was a problem hiding this comment.
Step router uses undefined context (will crash on any step execution)
executeStepByType takes _context but dispatches with context, which is not defined in this scope. Any execution of include_assets, build_manifest, etc. will throw a ReferenceError: context is not defined, breaking all builds using the pipeline.
Evidence (current code):
export async function executeStepByType(step: ClientStep, _context: BuildContext): Promise<unknown> {
switch (step.type) {
case 'include_assets':
return executeIncludeAssetsStep(step, context) // <-- context is undefinedSame bug repeats for every case.
Impact:
- All users invoking these step types will have builds fail immediately; CLI build pipeline becomes unusable.
- 100% reproducible whenever any implemented step type is run.
| <<<<<<< HEAD | ||
| ======= | ||
| | 'build_manifest' | ||
| >>>>>>> f89cf6d0c6 (Add a build manifest build step) |
There was a problem hiding this comment.
Unresolved merge conflict markers in ClientStep.type
packages/app/src/cli/services/build/client-steps.ts contains Git conflict markers (<<<<<<<, =======, >>>>>>>) inside the ClientStep.type union. This will either fail tsc parsing immediately or be committed and block all consumers of this file. Since this file defines the step pipeline contract, it’s a hard stop for deployment.
| } | ||
|
|
||
| const value = getNestedValue(config, key) | ||
| return typeof value === 'string' ? value : undefined |
There was a problem hiding this comment.
forEach mode drops top-level string[] module values (spec mismatch)
In resolveModule(), forEach mode correctly returns string[] when found on the current item, but the fallback to the top-level config only returns a string and drops string[] entirely:
- Item case supports: Array.isArray(value) && value.every(val => typeof val === 'string')
- Fallback case uses: return typeof value === 'string' ? value : undefined
This contradicts the stated behavior that string[] expands into multiple asset entries and that forEach checks item first then falls back to top-level config. If moduleKey points to a top-level string[] then resolveModule() returns undefined, resolveAssets() skips the asset (possibly only logging), and manifests can silently miss entries, causing downstream include_assets/manifest_result to miss static assets.
ryancbahan
left a comment
There was a problem hiding this comment.
Is there a tech design for this? It seems we're introducing our own DSL here, and I'd like to understand the scope and future plans of how we maintain this.

Add
build_manifestbuild stepSummary
This PR adds the
build_manifeststep type to the client steps pipeline introduced in #01-build-steps-infrastructure and wired in #02-wire-build-config-into-extension-specs.The step generates a JSON manifest that maps asset keys to their resolved filepaths and source modules. It supports two modes: a flat single-extension manifest and a forEach mode that iterates a config array and writes one entry per item. A
manifest_resultinclusion type is also added toinclude_assetsso downstream steps can consume the manifest output to copy static assets.Depends on:
02-wire-build-config-into-extension-specsNew step:
build_manifestConfig schema
Single mode
One manifest file for the whole extension:
Output written to
build-manifest.json:{ "assets": { "main": {"filepath": "my-ext-main.js", "module": "src/index.tsx"}, "tools": {"filepath": "my-ext-tools.json", "module": "src/tools.json", "static": true} } }forEach mode
One entry per item in a config array:
Output:
[ { "target": "purchase.checkout.block.render", "build_manifest": { "assets": { "main": {"filepath": "my-ext-purchase.checkout.block.render-main.js", "module": "src/checkout.tsx"} } } } ]The forEach shape mirrors what
UIExtensionSchema.transformattaches to eachextension_point, making it straightforward to feed the manifest back intoextension.configuration.extension_points[].build_manifest.Asset filepath naming
{handle}-{assetKey}{ext}{handle}-{keyByValue}-{assetKey}{ext}{handle}-{keyByValue}-{assetKey}-{index}{ext}extis.jsfor non-static assets; the source file's own extension forstatic: true.Module resolution
moduleKeysupports dot-notation ("should_render.module"). In forEach mode the current iteration item is checked first, falling back to the top-level extension config. When a key resolves to astring[], each value expands into a separate asset entry with an index suffix.Step output type
The result is stored in
context.stepResultsunder the step'sid, available to downstream steps.include_assets— newmanifest_resultinclusion typeAdds a fourth inclusion type to the
include_assetsstep that consumes a priorbuild_manifeststep's output and copies allstatic: trueassets:For each asset in the manifest where
static: true:{extension.directory}/{asset.module}{outputDir}/{asset.filepath}Works with both single-mode and forEach-mode manifest outputs. Assets without a
modulefield are skipped (considered already in place from the bundler).Typical usage — copy static assets after bundling:
getNestedValueutilityExtracted to
steps/utils.ts. Resolves dot-separated paths from a config object with TOML array-of-tables support:"should_render.module"→ navigates nested objectsundefinedfor unresolved pathsFiles changed
services/build/steps/build-manifest-step.tsservices/build/steps/utils.tsgetNestedValue()dot-notation resolverservices/build/steps/include_assets_step.tsmanifest_resultinclusion typeservices/build/steps/index.tsbuild_manifestcaseservices/build/client-steps.ts'build_manifest'toClientStep.typebuild-manifest-step.test.ts(610 lines),include-assets-step.test.tsextendedTest plan
build_manifest— single mode, forEach mode, inner array expansion, dot-notation resolution, static/optional flags, filepath naming, missing optional assets silently omittedinclude_assetsmanifest_result— static assets copied from single-mode output, static assets copied from forEach-mode output, assets withoutmoduleskipped🤖 Generated with Claude Code
Measuring impact
How do we know this change was effective? Please choose one:
Checklist